#ifndef __THREAD_POOL_HPP__
#define __THREAD_POOL_HPP__

// https://mp.weixin.qq.com/s/Hhoa297oXX7RLiDv7RbXIg

#include <iostream>
#include <thread>
#include <utility>
#include <vector>
#include <functional>
#include <queue>
#include <cassert>
#include <mutex>
#include <algorithm>
#include <condition_variable>
#include <chrono>

struct Task
{
    int _priority{0};
    std::function<void()> _execute_task;

    Task() = default;
    Task(std::function<void()> &&task)
        : _execute_task{task}
    {
    }

    Task(std::function<void()> &&task, int priority)
        : _execute_task{task},
          _priority{priority}
    {
    }
};

struct Job
{
    Task _task{};
    time_t _creat_time{};

    bool operator<(const Job &other) const
    {
        return this->_task._priority == other._task._priority ? this->_creat_time < other._creat_time : this->_task._priority < other._task._priority;
    }

    Job(Task task)
        : _task{std::move(task)}
    {
        this->_creat_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
    }
};

class ThreadPool
{
public:
    ThreadPool(size_t thread_num);
    ~ThreadPool();

    ThreadPool(const ThreadPool &other) = delete;

    ThreadPool(ThreadPool &&other) = delete;
    void operator=(ThreadPool &&) = delete;

    void start();

    void force_stop_gracefully();
    void force_stop();

    void add_task(std::function<void()> &&task, int priority = 0);
    void add_task_unsafe(std::function<void()> &&task, int priority = 0);

    void sync();

    void add_thread_safe(size_t thread_num);
    
    [[nodiscard]] size_t get_cur_thread_num() const;
    [[nodiscard]] size_t get_task_queue_size();

private:
    std::priority_queue<Job> _task_queue{};
    std::vector<std::thread> _thread_list{};
    std::mutex _thread_lock{};
    std::condition_variable _cv;
    std::atomic<bool> _is_stop{false};
    bool _is_started{false};
    size_t _thread_num{0};
    std::function<void()> _get_execute_func();

    void _add_thread_unsafe(size_t thread_num);
};

//
ThreadPool &get_thread_pool(size_t thread_num);
void test();

#endif